package com.mapforge.map.tools.selection;

import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.mapforge.selection.ComparablePoint;
import com.mapforge.selection.ContinuousSelection;
import com.mapforge.selection.DiscontinuousSelectionCollection;
import com.mapforge.selection.DiscontinuousSelectionSet;
import com.mapforge.selection.Selection;

/** */
public class EllipseSelectionTool extends AbstractSelectionTool {
	public EllipseSelectionTool() {
		super("Elliptical tile selection");
	}

	public Selection performSelection(Point startPoint, Point currentPoint) {
		Rectangle r = new Rectangle(startPoint.x, startPoint.y, 0, 0);
		r.add(currentPoint);
		return calculateEllipse(r.x, r.y, r.width + 1, r.height + 1);
	}

	public static boolean withinEllipse(int x, int y, int cx, int cy, int xRad,
			int yRad) {
		double dx = x - cx;
		double dy = y - cy;
		double tempx = (dx * dx) / (xRad * xRad);
		double tempy = (dy * dy) / (yRad * yRad);
		if (tempx < .05)
			tempx = 0;
		if (tempy < .05)
			tempy = 0;
		return tempx + tempy <= 1;
	}

	public Selection calculateEllipse(int x, int y, int width, int height) {
		// ensure the relative center is found at a greater value
		int actualH = height;
		int actualW = width;
		if ((width & 1) != 1) {// if even width
			width--;
		}
		if ((height & 1) != 1) {// if even height
			height--;
		}
		int cx = x + (width >> 1);
		int cy = y + (height >> 1);

		int centerX = cx - x + 1;
		int centerY = cy - y + 1;
		if (actualW > 2 && actualH > 2) {
			// approximately a quarter of the ellipse could be empty because of
			// edges
			List<ComparablePoint> selectionPnts = new ArrayList<ComparablePoint>(
					centerX * centerY * 3);
			DiscontinuousSelectionCollection selection = new DiscontinuousSelectionSet();
			ComparablePoint temp = new ComparablePoint();

			int xRadius = width >> 1;
			int yRadius = height >> 1;
			// calculate top left corner
			for (int i = 0; i < centerY; i++)
				for (int j = 0; j < centerX; j++) {
					if (withinEllipse(x + j, y + i, cx, cy, xRadius, yRadius)) {
						selectionPnts.add(new ComparablePoint(x + j, y + i));
					}
				}

			// reflect top left to top right
			for (int i = 0; i < centerY; i++) {
				for (int j = centerX; j < actualW; j++) {
					temp.setLocation(actualW - j + x - 1, y + i);
					if (selectionPnts.contains(temp)) {
						selectionPnts.add(new ComparablePoint(x + j, y + i));
					}
				}
			}

			// reflect top to bottom
			for (int i = centerY; i < actualH; i++) {
				for (int j = 0; j < actualW; j++) {
					temp.setLocation(x + j, actualH - i + y - 1);
					if (selectionPnts.contains(temp)) {
						selectionPnts.add(new ComparablePoint(x + j, y + i));
					}
				}
			}
			Iterator<ComparablePoint> pnts = selectionPnts.iterator();
			while (pnts.hasNext()) {
				selection.addPoint(pnts.next());
			}
			return selection;
		}
		// if either dimension < 3, fill all within bounding rect
		else {
			ContinuousSelection selection = new ContinuousSelection(
					new Rectangle(x, y, actualW, actualH));
			return selection;
		}

	}

	@Override
	public void performEdit() {
		// Intentionally left blank
	}
}
